#include "monitor.h"
#include "temu.h"
#include "watchpoint.h" 
#include "expr.h"
#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>

void cpu_exec(uint32_t);

void display_reg();
void sdb_watchpoint_display();

void delete_watchpoint(int);

void create_watchpoint(char*);

/* We use the `readline' library to provide more flexibility to read from stdin. */
char* rl_gets() {
	static char *line_read = NULL;

	if (line_read) {
		free(line_read);
		line_read = NULL;
	}

	line_read = readline("(temu) ");

	if (line_read && *line_read) {
		add_history(line_read);
	}

	return line_read;
}

static int cmd_c(char *args) {
	cpu_exec(-1);
	return 0;
}

static int cmd_q(char *args) {
	return -1;
}

static int cmd_help(char *args);

//单步执行
static int cmd_si(char *args);
//打印程序状态
static int cmd_info(char *args);
//表达式求值
static int cmd_p(char *args);
//扫描内存
static int cmd_x(char *args);
//设置监视点
static int cmd_w(char *args);
//删除监视点
static int cmd_d(char *args);

static struct {
	char *name;
	char *description;
	int (*handler) (char *);
} cmd_table [] = {
	{ "help", "Display informations about all supported commands", cmd_help },
	{ "c", "Continue the execution of the program", cmd_c },
	{ "q", "Exit TEMU", cmd_q },
	/* TODO: Add more commands */
	{ "si","exec N step",cmd_si},
	{ "info","print program state",cmd_info},
	{ "p","expression evaluation",cmd_p},
	{ "x","scan memory",cmd_x},
	{ "w","set watch point",cmd_w},
	{ "d","delete watch point",cmd_d}

	

};

#define NR_CMD (sizeof(cmd_table) / sizeof(cmd_table[0]))

static int cmd_help(char *args) {
	/* extract the first argument */
	char *arg = strtok(NULL, " ");
	int i;

	if(arg == NULL) {
		/* no argument given */
		for(i = 0; i < NR_CMD; i ++) {
			printf("%s - %s\n", cmd_table[i].name, cmd_table[i].description);
		}
	}
	else {
		for(i = 0; i < NR_CMD; i ++) {
			if(strcmp(arg, cmd_table[i].name) == 0) {
				printf("%s - %s\n", cmd_table[i].name, cmd_table[i].description);
				return 0;
			}
		}
		printf("Unknown command '%s'\n", arg);
	}
	return 0;
}
//单步执行
static int cmd_si(char *args)
{
	int steps;
	//这里传递NULL就是继续处理之前那个字符串
	char *arg = strtok(NULL, " "); //提取args
	if(arg == NULL)
	{
		steps = 1;
	}
	else//把arg指向的数字提取出来
	{
		steps = atoi(arg);//转换为整数
	}
	if(steps < -1)
	{
		printf("输入错误\n");
		return 0;
	}
	cpu_exec(steps);
	return 0;
}
//打印程序状态 r寄存器 w监视点
static int cmd_info(char *args)
{

	char *arg = strtok(NULL, " ");
	if(arg == NULL)
	{
		printf("Unknown command\n");
	}
	else
	{
		if(strcmp(arg, "r") == 0)
		{
			//打印所有寄存器状态
			display_reg();
		}
		else if(strcmp(arg, "w") == 0)
		{
			//打印所有监视点信息
			sdb_watchpoint_display();
		}
	}
	return 0;
}
//扫描内存

static int cmd_x(char *args)
{
	char *arg1 = strtok(NULL, " ");//arg1是N，arg2是EXPR
	char *arg2 = strtok(NULL, " ");
	size_t len;
	uint32_t addr,vaddr;
	sscanf(arg1,"%ld",&len);
	sscanf(arg2,"%x",&addr);
	vaddr = addr & 0x7FFFFFFF;
	size_t i;
	for(i=0;i<len;i++)
	{
		printf("%x:\t\t",vaddr);
		printf("%08x ",mem_read(vaddr,4));//mem_read读4个字节
		printf("\n");
		vaddr += 4;
	}
	return 0;
}
static int cmd_w(char *args)
{
	create_watchpoint(args);
    return 0;
}
static int cmd_p(char *args)
{
	if(args == NULL){
        printf("No args\n");
        return 0;
    }
    bool flag = false;
    uint32_t ans = expr(args, &flag);
	printf("ans = %d\t0x%08x\n",ans,ans);
    return 0;

}


static int cmd_d(char *args)
{
	if(args == NULL)
        printf("No args.\n");
    else
        delete_watchpoint(atoi(args));
    return 0;
}

void ui_mainloop() {//用户界面主循环 类似GDB
	FILE *fp=fopen("Gloden trace","w");
	fprintf(fp,"pc值\t\t寄存器编号\t\t待写入寄存器的值\n");
	fflush(fp);
	
	while(1) {
		char *str = rl_gets();
		char *str_end = str + strlen(str);

		/* extract the first token as the command */
		char *cmd = strtok(str, " ");
		if(cmd == NULL) { continue; }

		/* treat the remaining string as the arguments,
		 * which may need further parsing
		 */
		char *args = cmd + strlen(cmd) + 1;
		if(args >= str_end) {
			args = NULL;
		}

		int i;
		for(i = 0; i < NR_CMD; i ++) {
			if(strcmp(cmd, cmd_table[i].name) == 0) {
				if(cmd_table[i].handler(args) < 0) { return; }
				break;
			}
		}

		if(i == NR_CMD) { printf("Unknown command '%s'\n", cmd); }
	}
}
